﻿import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from datetime import datetime, timedelta

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体
plt.rcParams['axes.unicode_minus'] = False    # 解决负号显示问题

print("=" * 50)
print("Seaborn 综合使用示例 (使用自定义生成数据)")
print("=" * 50)

# 设置 Seaborn 样式
sns.set_style("whitegrid")
sns.set_palette("husl")

# 创建自定义数据集
print("\n1. 创建自定义数据集")
np.random.seed(42)  # 确保结果可重现

# 创建鸢尾花类似数据集
n_samples = 150
iris_data = {
    'sepal_length': np.concatenate([
        np.random.normal(5.0, 0.35, 50),
        np.random.normal(5.9, 0.5, 50),
        np.random.normal(6.6, 0.55, 50)
    ]),
    'sepal_width': np.concatenate([
        np.random.normal(3.4, 0.38, 50),
        np.random.normal(2.8, 0.31, 50),
        np.random.normal(3.0, 0.32, 50)
    ]),
    'petal_length': np.concatenate([
        np.random.normal(1.5, 0.17, 50),
        np.random.normal(4.3, 0.47, 50),
        np.random.normal(5.6, 0.55, 50)
    ]),
    'petal_width': np.concatenate([
        np.random.normal(0.2, 0.1, 50),
        np.random.normal(1.3, 0.2, 50),
        np.random.normal(2.0, 0.27, 50)
    ]),
    'species': ['setosa'] * 50 + ['versicolor'] * 50 + ['virginica'] * 50
}
iris_df = pd.DataFrame(iris_data)

# 创建小费类似数据集
n_tips = 200
tips_data = {
    'total_bill': np.random.normal(20, 6, n_tips),
    'tip': np.random.normal(3, 1, n_tips),
    'sex': np.random.choice(['Male', 'Female'], n_tips),
    'smoker': np.random.choice(['Yes', 'No'], n_tips),
    'day': np.random.choice(['Sun', 'Sat', 'Thur', 'Fri'], n_tips),
    'time': np.random.choice(['Lunch', 'Dinner'], n_tips),
    'size': np.random.randint(1, 6, n_tips)
}
tips_df = pd.DataFrame(tips_data)
tips_df['tip'] = tips_df['tip'] + tips_df['total_bill'] * 0.1  # 使小费与总账单相关

# 创建航班类似数据集
years = range(2018, 2023)
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 
          'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
flights_data = []
for year in years:
    for month in months:
        base_passengers = 300 + (year - 2018) * 50  # 每年增长
        seasonal_factor = 1.2 if month in ['Jun', 'Jul', 'Aug'] else 0.9 if month in ['Jan', 'Feb'] else 1.0
        passengers = int(np.random.normal(base_passengers * seasonal_factor, 20))
        flights_data.append({'year': year, 'month': month, 'passengers': passengers})
flights_df = pd.DataFrame(flights_data)

print("自定义鸢尾花数据集形状:", iris_df.shape)
print("自定义小费数据集形状:", tips_df.shape)
print("自定义航班数据集形状:", flights_df.shape)

# 创建可视化图表
fig = plt.figure(figsize=(20, 16))
fig.suptitle('Seaborn 综合使用示例 (自定义数据)', fontsize=20)

# 1. 散点图与回归线
print("\n2. 创建散点图与回归线")
ax1 = plt.subplot(3, 4, 1)
sns.scatterplot(
    data=iris_df, 
    x='sepal_length', 
    y='sepal_width', 
    hue='species',
    style='species',
    s=100
)
plt.title('散点图: 萼片长度与宽度')

ax2 = plt.subplot(3, 4, 2)
sns.regplot(
    data=iris_df, 
    x='sepal_length', 
    y='sepal_width',
    scatter_kws={'s': 20},
    line_kws={'color': 'red'}
)
plt.title('回归图: 萼片长度与宽度')

# 2. 箱线图和小提琴图
print("\n3. 创建箱线图和小提琴图")
ax3 = plt.subplot(3, 4, 3)
sns.boxplot(
    data=iris_df, 
    x='species', 
    y='petal_length'
)
plt.title('箱线图: 不同物种的花瓣长度')

ax4 = plt.subplot(3, 4, 4)
sns.violinplot(
    data=iris_df, 
    x='species', 
    y='petal_length',
    inner='quartile'
)
plt.title('小提琴图: 不同物种的花瓣长度')

# 3. 直方图和密度图
print("\n4. 创建直方图和密度图")
ax5 = plt.subplot(3, 4, 5)
sns.histplot(
    data=iris_df, 
    x='sepal_length', 
    kde=True,
    hue='species',
    element='step'
)
plt.title('直方图: 萼片长度分布')

ax6 = plt.subplot(3, 4, 6)
sns.kdeplot(
    data=iris_df, 
    x='sepal_length', 
    hue='species',
    fill=True,
    alpha=0.5
)
plt.title('密度图: 萼片长度分布')

# 4. 条形图和计数图
print("\n5. 创建条形图和计数图")
ax7 = plt.subplot(3, 4, 7)
sns.barplot(
    data=tips_df, 
    x='day', 
    y='total_bill',
    hue='sex',
    errorbar='sd'
)
plt.title('条形图: 每日平均消费金额')

ax8 = plt.subplot(3, 4, 8)
sns.countplot(
    data=tips_df, 
    x='day',
    hue='sex'
)
plt.title('计数图: 每日用餐次数')

# 5. 热力图
print("\n6. 创建热力图")
# 准备数据
flights_pivot = flights_df.pivot("month", "year", "passengers")
ax9 = plt.subplot(3, 4, 9)
sns.heatmap(
    flights_pivot,
    annot=True,
    fmt="d",
    cmap="YlOrRd",
    linewidths=0.5
)
plt.title('热力图: 每月航班乘客数')

# 6. 配对图示例
print("\n7. 创建配对图示例")
ax10 = plt.subplot(3, 4, 10)
sns.scatterplot(
    data=iris_df, 
    x='sepal_length', 
    y='petal_length', 
    hue='species'
)
plt.title('配对图示例: 萼片与花瓣长度')

# 7. 分类散点图
print("\n8. 创建分类散点图")
ax11 = plt.subplot(3, 4, 11)
sns.stripplot(
    data=iris_df, 
    x='species', 
    y='petal_width',
    jitter=True,
    hue='species',
    dodge=False
)
plt.title('分类散点图: 花瓣宽度')

# 8. 小费与总账单关系
print("\n9. 小费与总账单关系")
ax12 = plt.subplot(3, 4, 12)
sns.scatterplot(
    data=tips_df, 
    x='total_bill', 
    y='tip', 
    hue='time',
    style='smoker',
    s=100
)
plt.title('小费与总账单关系')

plt.tight_layout()
plt.show()

# 9. 单独显示配对图
print("\n10. 单独显示配对图")
sns.pairplot(
    iris_df, 
    hue='species',
    diag_kind='hist',
    markers=['o', 's', 'D']
)
plt.suptitle('配对图: 自定义鸢尾花数据集所有特征关系', y=1.02)
plt.show()

# 10. 分面网格
print("\n11. 创建分面网格")
g = sns.FacetGrid(tips_df, col="time", row="smoker", margin_titles=True)
g.map(sns.scatterplot, "total_bill", "tip")
g.fig.suptitle('分面网格: 小费与总账单关系（按时间和吸烟者分组）', y=1.02)
plt.show()

# 11. 线性回归模型图
print("\n12. 创建线性回归模型图")
sns.lmplot(
    data=tips_df, 
    x="total_bill", 
    y="tip", 
    hue="smoker",
    col="time",
    markers=['o', 'x']
)
plt.suptitle('线性回归模型: 小费与总账单关系', y=1.02)
plt.show()

# 12. 时间序列图
print("\n13. 创建时间序列图")
# 准备时间序列数据
month_map = {month: i+1 for i, month in enumerate(months)}
flights_df['month_num'] = flights_df['month'].map(month_map)
flights_df['date'] = pd.to_datetime(flights_df['year'].astype(str) + '-' + flights_df['month_num'].astype(str) + '-01')

plt.figure(figsize=(12, 6))
sns.lineplot(
    data=flights_df, 
    x='date', 
    y='passengers',
    hue='year',
    style='year',
    markers=True
)
plt.title('时间序列: 航班乘客数量变化')
plt.xticks(rotation=45)
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()
plt.show()

print("\n" + "=" * 50)
print("Seaborn 综合示例完成")
print("=" * 50)